let electron = null, protectWindow = null;

if (process.type === 'renderer') {
    Object.assign(exports, require('./remote/index.js'));

    function randomKey() {
        let random = () => (Math.random() * 16 | 0).toString(16);
        return Date.now().toString(16) + random() + random() + random();
    }

    exports.Notification = class {
        constructor(options) {
            this.id = randomKey();
            this.options = options;
            this.ipc = require('electron').ipcRenderer;
            this.listeners = {
                click: () => {},
                reply: () => {}
            };

            let channel = 'jelly_notification_event_' + this.id;
            this.ipc.on(channel, (event, type, data) => {
                // 发生任意一个操作都需要移除事件，以免产生内存泄漏
                this.ipc.removeAllListeners(channel);

                let listener = this.listeners[type];
                if (typeof listener === "function") {
                    listener(event, data);
                }
            });
        }

        show() {
            this.ipc.send('command', 'showNotification', this.id, this.options);
        }

        close() {
            this.ipc.send('command', 'closeNotification', this.id);
        }

        on(type, fun) {
            this.listeners[type] = fun;
        }
    };

    let version = null, appName = null, productName = process.productName;
    exports.app = {
        get version() {
            if (version === null) {
                version = require('electron').remote.app.getVersion();
            }
            return version;
        },

        get name() {
            if (appName === null) {
                appName = require('electron').remote.app.getName();
            }
            return appName;
        },

        productName,

        tray: {
            flash(flag, cb) {
                const ipc = require('electron').ipcRenderer, channel = 'jelly_tray_event';
                ipc.removeAllListeners(channel);

                if (flag && typeof cb === 'function') {
                    ipc.once(channel, (event, type, data) => {
                        cb();
                    });
                }

                ipc.send('command', 'flashTray', flag);
            },

            setImage(icon) { },

            update(...args) {
                const ipc = require('electron').ipcRenderer;
                ipc.send('command', 'updateTray', ...args);
            }
        },

        quit() {
            require('electron').remote.app.quit();
        },

        getLoginItemSettings(options) {
            if (electron === null) electron = require('electron').remote;
            electron.app.getLoginItemSettings(options);
        },

        setLoginItemSettings(settings) {
            if (electron === null) electron = require('electron').remote;
            electron.app.setLoginItemSettings(settings);
        }
    };
} else {
    electron = require('electron');
    const events = require("events");
    const {app, BrowserWindow, Notification, nativeImage, screen} = electron;
    const notifications = new Map(), windows = new Map();

    let appPath, debug = process.env.npm_package_debug === true || process.env.npm_package_debug === 'true';
    let version = null, appName = null, productName = process.productName;

    exports.Notification = electron.Notification;

    class WindowAgent {
        constructor(win) {
            this.win = win.win;

            const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(win)).filter(value => defaultWindowMethods.indexOf(value) === -1);
            for (let name of methods) {
                this[name] = (...args) => {
                    this.win.webContents.executeJavaScript('document.defaultWindow.' + name + '.apply(document.defaultWindow, ' + JSON.stringify(args) + ')', true);
                };
            }
        }

        close() {
            this.win.close();
            this.win = null;
        }

        show() {
            this.win.show();
        }

        hide() {
            this.win.hide();
        }

        maximize() {
            this.win.maximize();
        }

        minimize() {
            this.win.minimize();
        }

        restore() {
            this.win.restore();
        }
    }

    const command = {
        openWindow(url, options, params) {
            let time = Date.now();

            let win = new BrowserWindow(Object.assign({
                show: options.displayed === true,
                frame: false,
                autoHideMenuBar: true,
                titleBarStyle: 'hiddenInset',
                // transparent: true,
                // backgroundColor: '#00000000',
                webPreferences: {
                    preload: __dirname + '/preload.js',
                    nodeIntegration: false,
                    nativeWindowOpen: true,
                    webviewTag: true
                }
            }, options.features));

            console.log('Init Time -> ' + (Date.now() - time));
            win.loadFile(appPath + '/index.html', {
                search: '?r=' + encodeURIComponent(url) + '&n=' + encodeURIComponent(productName) + '&p=' + encodeURIComponent(JSON.stringify(params || {}))
            });
            if (options.protected) {
                protectWindow = win;
                win.on('close', (event) => {
                    if (protectWindow) {
                        event.preventDefault();
                        win.hide();
                    }
                });
                win.on('closed', (event) => {
                    protectWindow = null;
                });
            } else {
                win.on('closed', () => win = null);
            }
            win.webContents.on('new-window', (event, url, frameName, disposition, options, additionalFeatures) => {
                // 临时方案，用于解决 additionalFeatures 无效的问题
                if (frameName === 'simple') {
                    event.preventDefault();
                    let {width, height} = electron.screen.getPrimaryDisplay().size;
                    Object.assign(options, {
                        x: 0,
                        y: 0,
                        width: width,
                        height: height,
                        titleBarStyle: 'default',
                        thickFrame: false,
                        enableLargerThanScreen: true,
                        alwaysOnTop: true,
                        resizable: false,
                        movable: false,
                        fullscreen: process.platform === 'win32'
                    });

                    event.newGuest = new BrowserWindow(options);
                    event.newGuest.setAlwaysOnTop(true, 'pop-up-menu');
                } else {
                    event.preventDefault();
                    let opt = JSON.parse(frameName);
                    if (opt.features.modal) {
                        opt.features.parent = windows.get(opt.openerId) || win;
                    }
                    opt.show = opt.displayed === true;
                    event.newGuest = new BrowserWindow(Object.assign(options, opt.features));
                    windows.set(opt.id, event.newGuest);
                    let browserView = null;
                    event.newGuest.on('closed', () => {
                        windows.delete(opt.id);
                        if (browserView) {
                            browserView.destroy();
                            browserView = null;
                        }
                    });
                    event.newGuest.webContents.on('will-navigate', function (e, url) {
                        if (url === 'jelly://show') {
                            e.preventDefault();
                            event.newGuest.show();
                        } else if (url === 'jelly://hide') {
                            e.preventDefault();
                            event.newGuest.hide();
                        } else if (url === 'jelly://minimize') {
                            e.preventDefault();
                            event.newGuest.minimize();
                        } else if (url === 'jelly://maximize') {
                            e.preventDefault();
                            event.newGuest.maximize();
                        } else if (url === 'jelly://unmaximize') {
                            e.preventDefault();
                            event.newGuest.unmaximize();
                        } else if (url === 'jelly://focus') {
                            e.preventDefault();
                            event.newGuest.focus();
                        } else if (url.indexOf('jelly://setAlwaysOnTop') === 0) {
                            e.preventDefault();
                            let args = url.replace('jelly://setAlwaysOnTop?', '').split(',');
                            args[0] = args[0] === 'true';
                            event.newGuest.setAlwaysOnTop(...args);
                        } else if (url.indexOf('jelly://setSize') === 0) {
                            e.preventDefault();
                            let args = url.replace('jelly://setSize?', '').split(',');
                            event.newGuest.setSize(...args);
                        } else if (url.indexOf('jelly://setBounds') === 0) {
                            e.preventDefault();
                            let args = JSON.parse(url.replace('jelly://setBounds?', ''));
                            event.newGuest.setBounds(args);
                        } else if (url.indexOf('jelly://setBrowserView') === 0) {
                            e.preventDefault();
                            let args = JSON.parse(decodeURIComponent(url.replace('jelly://setBrowserView?', '')));
                            const { BrowserView } = require('electron');
                            browserView = new BrowserView({
                                webPreferences: {
                                    webSecurity: false,
                                    preload: args.preload,
                                    nativeWindowOpen: true,
                                    partition: args.partition
                                }
                            });
                            event.newGuest.setBrowserView(browserView);
                            browserView.setBounds({ x: args.x, y: args.y, width: args.width, height: args.height });
                            browserView.setAutoResize({width: true, height: true});
                            browserView.webContents.loadURL(args.url);
                            browserView.webContents.once('dom-ready', () => {
                                win.webContents.executeJavaScript('if (' + args.callback + ') ' + args.callback + '(' + browserView.webContents.id + ')');
                            });
                            browserView.webContents.on('new-window', (event, url, frameName, disposition, options) => {
                                event.preventDefault();
                                options.webPreferences.sandbox = true;
                                event.newGuest = new BrowserWindow(Object.assign(options, {
                                    useContentSize: true
                                }));
                                event.newGuest.setMenu(null);
                            });
                            if (debug) browserView.webContents.openDevTools();
                        }
                    });
                    if (debug && opt.debug) event.newGuest.webContents.openDevTools();
                }
            });
            if (debug) win.webContents.openDevTools();
            return win;
        },

        openPath(path) {
            electron.shell.openPath(path);
        },

        openExternal(url, options) {
            electron.shell.openExternal(url, options);
        },

        showItemInFolder(fullPath) {
            electron.shell.showItemInFolder(fullPath);
        },

        showNotification(e, id, options) {
            let notify = new Notification(options);
            notify.on('click', (event, data) => {
                notify = null;
                notifications.delete(id);
                e.reply('jelly_notification_event_' + id, 'click', data);
            });
            notify.on('reply', (event, data) => {
                notify = null;
                notifications.delete(id);
                e.reply('jelly_notification_event_' + id, 'reply', data);
            });
            notify.on('close', (event, data) => {
                notify = null;
                notifications.delete(id);
                e.reply('jelly_notification_event_' + id, 'close', data);
            });

            notify.show();
            notifications.set(id, notify);
        },

        closeNotification(id) {
            let notify = notifications.get(id);
            if (notify) {
                notify.close();
            }
            notifications.delete(id);
        },

        flashTray(e, flag) {
            exports.app.tray.flash(flag, () => {
                e.reply('jelly_tray_event');
            });
        },

        updateTray(...args) {
            exports.app.tray.update(...args);
        },

        callSessionMethod(e, name, ...args) {
            let ret = typeof exports.session[name] === 'function' ? exports.session[name](...args) : undefined;
            e.returnValue = ret !== undefined ? ret : null;
        }
    };

    exports.Application = class {
        constructor() {
            let tray, flashTrayTask, moveEnable = false, enterListeners = [], leaveListeners = [];
            this.productName = productName;
            this.tray = {
                icon: null,
                updateListener: null,

                show(icon) {
                    this.icon = icon;
                    tray = new electron.Tray(icon);
                },

                on(type, listener) {
                    if (type === 'update') {
                        this.updateListener = listener;
                    } else if (process.platform === 'win32' && (type === 'mouse-enter' || type === 'mouse-leave')) {
                        if (!moveEnable) {
                            moveEnable = true;
                            let enter = false, timer;
                            tray.on('mouse-move', (event, position) => {
                                if (!enter) {
                                    enterListeners.forEach((fun) => {
                                        fun(event, position);
                                    });
                                }
                                enter = true;
                                clearInterval(timer);
                                timer = setInterval(() => {
                                    let {x, y} = screen.getCursorScreenPoint(), bounds = tray.getBounds();
                                    let minX = bounds.x;
                                    let minY = bounds.y;
                                    let maxX = minX + bounds.width;
                                    let maxY = minY + bounds.height;

                                    if (x < minX  || x > maxX || y < minY || y > maxY) {
                                        clearInterval(timer);
                                        enter = false;
                                        leaveListeners.forEach((fun) => {
                                            fun(event, {x, y});
                                        });
                                    }
                                }, 200);
                            });
                        }
                        if (type === 'mouse-enter') {
                            enterListeners.push(listener);
                        } else {
                            leaveListeners.push(listener);
                        }
                    } else {
                        tray.on(type, listener);
                    }
                },

                flash(flag, cb) {
                    clearInterval(flashTrayTask);

                    if (flag) {
                        let changed = false, empty = nativeImage.createEmpty();
                        flashTrayTask = setInterval(() => {
                            if (changed) {
                                changed = false;
                                tray.setImage(this.icon);
                            } else {
                                changed = true;
                                tray.setImage(empty);
                            }
                        }, 450);

                        tray.once('click', () => {
                            clearInterval(flashTrayTask);
                            tray.setImage(this.icon);
                            cb();
                        });
                    } else {
                        tray.setImage(this.icon);
                    }
                },

                setImage(icon) {
                    this.icon = icon;
                    tray.setImage(icon);
                },

                update(...args) {
                    if (typeof this.updateListener === 'function') {
                        this.updateListener(...args);
                    }
                },

                popUpContextMenu(menu) {
                    tray.popUpContextMenu(menu);
                },

                getLoginItemSettings(options) {
                    return app.getLoginItemSettings(options);
                },

                setLoginItemSettings(settings) {
                    app.setLoginItemSettings(settings);
                }
            }
        }

        get version() {
            if (version === null) {
                version = electron.app.getVersion();
            }
            return version;
        }

        get name() {
            if (appName === null) {
                appName = electron.app.getName();
            }
            return appName;
        }

        quit() {
            app.quit();
        }

        onLaunch() { }

        onActivate() { }

        onWakeup() { }

        onExit() { }
    };

    exports.Window = class extends events.EventEmitter {
        constructor() {
            super();

            this.id = null;

            this.title = null;
            this.x = null;
            this.y = null;
            this.width = 1000;
            this.height = 720;
            this.minWidth = 0;
            this.minHeight = 0;
            this.resizable = true;
            this.minimizable = true;
            this.closable = true;
            this.fullscreen = false;
            this.modal = false;
            this.focusable = true;
            this.alwaysOnTop = false;

            this.win = null;
            this.dom = null;
            this.protected = false;
            this.debug = true;

            this.focused = false;
            this.visible = false;
            this.minimized = false;

            this.displayed = true;
        }

        static Show(WindowProto, params) {
            let win = new WindowProto(params);
            win.open(params);

            return new WindowAgent(win);
        }

        static Open(WindowProto, params) {
            let win = new WindowProto(params);
            win.displayed = false;
            win.open(params);

            return new WindowAgent(win);
        }

        static GetWindow(id) {

        }

        static CloseAllWindow() {
            let windows = BrowserWindow.getAllWindows();
            for (let win of windows) {
                win.close();
            }
        }

        open(params) {
            this.win = command.openWindow(this.cwd(true), {
                protected: this.protected,
                debug: this.debug,
                displayed: this.displayed,
                features: {
                    title: this.title,
                    width: this.width,
                    height: this.height,
                    minWidth: this.minWidth,
                    minHeight: this.minHeight,
                    resizable: this.resizable,
                    minimizable: this.minimizable,
                    closable: this.closable,
                    fullscreen: this.fullscreen,
                    modal: this.modal,
                    focusable: this.focusable,
                    alwaysOnTop: this.alwaysOnTop
                }
            }, params);
        }

        close() {
            this.win.close();
            this.win = null;
        }

        show() {
            this.win.show();
        }

        hide() {
            this.win.hide();
        }

        maximize() {
            this.win.maximize();
        }

        minimize() {
            this.win.minimize();
        }

        restore() {
            this.win.restore();
        }

        setSize() {

        }

        onCreated() { }

        onOpen() { }

        onShow() { }

        onClose() { }

        onMaximize() { }

        onMinimize() { }

        onRestore() { }

        onFocus() { }

        onBlur() { }

        render() { }

        cwd() { }
    };

    const defaultWindowMethods = Object.getOwnPropertyNames(exports.Window.prototype);

    exports.Page = class { };

    exports.Component = class { };

    // 处理单个应用程序实例
    if (app.requestSingleInstanceLock()) {
        app.on('second-instance', (event, argv, workingDirectory) => {
            if (process.platform === 'win32') {
                const offset = app.isPackaged ? 1 : 2;
                const url = argv.find((arg, i) => i >= offset && arg.startsWith(exports.app.name.toLowerCase()));
                if (url) return exports.app.onWakeup(url);
            }

            exports.app.onActivate();
        });

        exports.InitComponent = (delegate, path) => {
            appPath = path;
            app.setAppUserModelId(process.execPath);
            app.on('ready', () => {
                delegate.onLaunch();
            });
            app.on('window-all-closed', () => app.quit());
            app.on('before-quit', function () {
                if (protectWindow) {
                    protectWindow.removeAllListeners('close');
                    protectWindow = null;
                }
            });
            app.on('activate', function () {
                delegate.onActivate();
            });
            // 注册自定义协议
            if (typeof delegate.onWakeup !== exports.Application.prototype.onWakeup) {
                const args = [];
                if (!app.isPackaged) {
                    args.push(require('path').resolve(process.argv[1]));
                }
                args.push('--');
                app.setAsDefaultProtocolClient(delegate.name.toLowerCase(), process.execPath, args);
                app.on('open-url', function (event, url) {
                    event.preventDefault();
                    delegate.onWakeup(url);
                });
            }

            electron.ipcMain.on("command", function (e, name, ...args) {
                if (name === 'showNotification' || name === 'flashTray' || name === 'callSessionMethod') {
                    command[name](e, ...args);
                } else if (typeof command[name] === 'function') {
                    command[name](...args);
                }
            });

            Object.assign(exports.app, delegate);
            exports.app = delegate;
        };
    } else {
        exports.InitComponent = () => {};
        app.quit();
    }

    exports.app = {};
    exports.openPath = command.openPath;
    exports.openExternal = command.openExternal;
    exports.showItemInFolder = command.showItemInFolder;
    exports.Tray = electron.Tray;

    exports.StateList = class {};
}

exports.chooseFile = function(win, name = '文件', extensions = ['*'], multiple = false) {
    if (electron === null) electron = require('electron').remote;
    const {dialog} = electron;

    if (typeof extensions === 'boolean') {
        multiple = extensions;
        extensions = ['*'];
    }

    let options = { title: '选择' + name, properties: ['openFile', 'createDirectory', 'noResolveAliases', 'treatPackageAsDirectory'], filters: [{name, extensions}]};
    if (multiple) {
        options.properties.push('multiSelections');
    }

    return new Promise(function (resolve) {
        dialog.showOpenDialog(options).then((result) => {
            resolve(multiple ? result.filePaths : (result.filePaths[0] || null));
        });
    });
};

exports.chooseImage = function(win, multiple = false) {
    return exports.chooseFile(win, '图片', ['jpg', 'png', 'jpeg', 'gif', 'bmp', 'tif'], multiple);
};

exports.chooseFolder = function(win, title = '保存文件', defaultPath, multiple = false) {
    if (electron === null) electron = require('electron').remote;
    const {dialog} = electron;

    if (typeof defaultPath === 'boolean') {
        multiple = defaultPath;
        defaultPath = undefined;
    }

    let options = { title, defaultPath, properties: ['createDirectory', 'openDirectory']};

    return new Promise(function (resolve) {
        if (multiple) {
            options.properties.push('multiSelections');
            dialog.showOpenDialog(options).then((result) => {
                resolve(result.filePaths);
            });
        } else {
            dialog.showSaveDialog(options).then((result) => {
                resolve(result.filePath);
            });
        }

    });
};

exports.ContextMenu = class ContextMenu {
    constructor() {
        if (electron === null) electron = require('electron').remote;
        this.context = new electron.Menu();
    }

    /**
     *
     * @param {string} text - 菜单文本
     * @param {ContextMenu} [sub] - 子菜单
     * @param {boolean} [separator] - 是否增加分割线
     * @param {function} [click] - 点击事件
     */
    addItem(text, sub, separator, click) {
        const { MenuItem } = electron;

        if (typeof sub === 'function') {
            click = sub;
            sub = null;
            separator = false;
        } else if (typeof sub === 'boolean') {
            click = separator;
            separator = sub;
            sub = null;
        }
        if (typeof separator === 'function') {
            click = separator;
            separator = false;
        }

        let menu = {label: text, click};
        if (sub instanceof ContextMenu) {
            menu.submenu = sub.context;
        } else if (typeof sub === 'string') {
            menu.sublabel = sub;
        } else {
            // keep menu width
            menu.label = text + '          '.substr(0, 15 - text.length);
        }
        this.context.append(new MenuItem(menu));
        if (separator) this.context.append(new MenuItem({type: 'separator'}));
    }

    show(tray) {
        if (tray) {
            tray.popUpContextMenu(this.context);
        } else {
            this.context.popup();
        }
    }
};

exports.MessageBox = {
    show(options) {
        if (electron === null) electron = require('electron').remote;
        const { dialog, nativeImage } = electron;
        let {icon, title, message, confirmText, cancelText} = options;
        let opt = {title, message, buttons: ['确定'], cancelId: 1, noLink: true};
        if (typeof icon === 'string') opt.icon = nativeImage.createFromPath(icon);
        if (typeof confirmText === 'string') opt.buttons[0] = confirmText;
        if (typeof cancelText === 'string') opt.buttons.push(cancelText);

        return new Promise((resolve) => {
            dialog.showMessageBox(opt).then((result) => {
                resolve(result.response === 0);
            });
        });
    },

    alert(title, message, options) {
        return this.show(Object.assign({title, message, confirmText: '确定'}, options));
    },

    confirm(title, message, options) {
        return this.show(Object.assign({title, message, confirmText: '确定', cancelText: '取消'}, options));
    },

    prompt() {

    }
};

exports.dock = {
    setBadgeCount: (count) => {
        if (electron === null) electron = require('electron').remote;
        electron.app.setBadgeCount(count);
    },

    bounce: (continued = false) => {
        if (electron === null) electron = require('electron').remote;
        electron.app.dock.bounce(continued === true ? 'critical' : 'informational');
    }
};

exports.session = new (require('./Session.js'))();

exports.isAeroGlassEnabled = function () {
    return electron.systemPreferences.isAeroGlassEnabled();
};

exports.os = {
    get directory() {
        return {
            HOME: this.getPath('home'),
            APP_DATA: this.getPath('appData'),
            USER_DATA: this.getPath('userData'),
            DESKTOP: this.getPath('desktop'),
            DOCUMENTS: this.getPath('documents'),
            DOWNLOADS: this.getPath('downloads')
        }
    },

    getPath(name) {
        if (electron === null) electron = require('electron').remote;
        return electron.app.getPath(name).replace(/\\/g, '/');
    }
};

